#include "EventLoop.hpp"
#include "Acceptor.hpp"
#include <func.h>

namespace ln{

EventLoop::EventLoop(Acceptor & ac)
:_epfd(createEpollFd())
,_acceptor(ac)
,_isLooping(false)
,_evtArr(1024)
{
    addEpollReadEvent(_acceptor.fd());
}

EventLoop::~EventLoop(){
    if(_epfd > 0){
        close(_epfd);
    }
}
    
void EventLoop::loop(){
    _isLooping = true;
    while(_isLooping){
        waitEpollFd();
    }
}

void EventLoop::unloop(){
    _isLooping = false;
}

int EventLoop::createEpollFd(){
    int epfd = epoll_create1(0);
    if(epfd < 0){
        perror("epoll_create1");
    }
    return epfd;
}

void EventLoop::addEpollReadEvent(int fd){
    struct epoll_event ev;
    memset(&ev,0,sizeof(ev));
    ev.data.fd = fd;
    ev.events = EPOLLIN;
    
    int ret = epoll_ctl(_epfd,EPOLL_CTL_ADD,fd,&ev);
    if(ret < 0){
        perror("epoll_ctl");
    }
}

void EventLoop::delEpollReadEvent(int fd){
    struct epoll_event ev;
    memset(&ev,0,sizeof(ev));
    ev.data.fd = fd;
    ev.events = EPOLLIN;

    int ret = epoll_ctl(_epfd,EPOLL_CTL_DEL,fd,&ev);
    if(ret < 0){
        perror("epoll_ctl");
    }
}

void EventLoop::waitEpollFd(){
    int nready = epoll_wait(_epfd,_evtArr.data(),_evtArr.size(),5000);
    if(nready == -1 && errno == EINTR){
        return;
    }else if(nready == -1){
        perror("epoll_wait");
        return;
    }else if(nready == 0){
        printf("timeout");
    }else{
        for(int i = 0; i < nready; ++i){
            int fd = _evtArr[i].data.fd;
            if(fd == _acceptor.fd()){
                handleNewConnection();
            }else{
                handleMessage(fd);
            }
        }
    }
}

void EventLoop::handleNewConnection(){
    //01 建立新连接
    int netfd = _acceptor.accept();
    if(netfd < 0){
        return;
    }

    //02 加入红黑树进行监听
    addEpollReadEvent(netfd);

    //03 创建TcpConnection
    TcpConnectionPtr conn(new TcpConnection(netfd));

    //04 注册coon的三个函数对象
    conn->setAllCallbacks(_onConnection,_onMessage,_onClose);

    //05 执行新连接建立时的函数对象
    conn->handleNewConnectionCallback();

    //06 加入map管理conn
    _conns[netfd] = conn;
}

void EventLoop::handleMessage(int fd){
    auto it = _conns.find(fd);
    if(it != _conns.end()){
        //找到了

        //判断连接是否断开
        bool isClosed = it->second->isClosed();
        if(isClosed){
            //连接断开
            it->second->handleCloseCallback();
            delEpollReadEvent(fd);
            _conns.erase(fd);
        }else{
            //连接未断开
            it->second->handleMessageCallback();
        }
    }
}

}//end of namespace ln
